/**
 * Copyright 2010 Jake Jackson
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jake.nb.ext.ant.hints.ui;

import java.awt.Component;
import java.util.Collection;
import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JTree;
import javax.swing.ListCellRenderer;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeSelectionModel;
import org.jake.nb.ext.ant.hints.AntHint;
import org.jake.nb.ext.ant.hints.Paths;
import org.netbeans.spi.editor.hints.Severity;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;

final class AntPanel extends javax.swing.JPanel {

  private DefaultTreeCellRenderer dr = new DefaultTreeCellRenderer();
  private JCheckBox renderer = new JCheckBox();
  private HintsPanelLogic logic;

  AntPanel() {
    initComponents();
    descriptionArea.setContentType("text/html");

    severityCombobox.setRenderer(new ComboboxRenderer());
    severityCombobox.addItem(Severity.HINT);
    severityCombobox.addItem(Severity.WARNING);
    severityCombobox.addItem(Severity.ERROR);


    hintsTree.setRootVisible(false);
    hintsTree.setShowsRootHandles(true);
    hintsTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
    hintsTree.setCellRenderer(new HintsTreeRenderer());

    update();

    fillTreeModel();
  }

  synchronized void update() {
    if (logic != null) {
      logic.disconnect();
    }
    logic = new HintsPanelLogic();
    logic.connect(hintsTree, severityCombobox, descriptionArea);
  }

  void cancel() {
    logic.disconnect();
    logic = null;
  }

  boolean isChanged() {
    return logic != null ? logic.isChanged() : false;
  }

  void applyChanges() {
    logic.applyChanges();
    logic.disconnect();
    logic = null;
  }

  private void fillTreeModel() {
    final Map<String, Collection<AntHint>> pathMap = new TreeMap<String, Collection<AntHint>>(new Comparator<String>() {

      @Override
      public int compare(String o1, String o2) {
        int ret = 0;
        if (Paths.GENERAL.equals(o1)) {
          ret = -1;
        } else if (Paths.GENERAL.equals(o2)) {
          ret = 1;
        } else {
          ret = o1.compareTo(o2);
        }
        return ret;
      }
    });
    final DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
    final TreeModel hintsTreeModel = new DefaultTreeModel(rootNode);
    final Lookup lookup = Lookups.forPath("org-jake-nb-ext-ant-hints"); // NOI18N

    for (final AntHint antHint : lookup.lookupAll(AntHint.class)) {
      String path = antHint.getConfiguration().getPath();

      if (path == null || path.isEmpty()) {
        path = "";
      }

      Collection<AntHint> hintsOfPath = pathMap.get(path);
      if (hintsOfPath == null) {
        hintsOfPath = new TreeSet<AntHint>(new AntHintComparator());
        pathMap.put(path, hintsOfPath);
      }
      hintsOfPath.add(antHint);

//      rootNode.add(new DefaultMutableTreeNode(antHint));
    }

    for (final Map.Entry<String, Collection<AntHint>> entry : pathMap.entrySet()) {
      DefaultMutableTreeNode nodeToAddTo = null;
      if ("".equals(entry.getKey())) {
        nodeToAddTo = rootNode;
      } else {
        nodeToAddTo = new DefaultMutableTreeNode(entry.getKey());
        rootNode.add(nodeToAddTo);
      }

      for (final AntHint antHint : entry.getValue()) {
        nodeToAddTo.add(new DefaultMutableTreeNode(antHint));
      }
    }


    hintsTree.setModel(hintsTreeModel);
  }

  /** This method is called from within the constructor to
   * initialize the form.
   * WARNING: Do NOT modify this code. The content of this method is
   * always regenerated by the Form Editor.
   */
  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  private void initComponents() {
    java.awt.GridBagConstraints gridBagConstraints;

    jSplitPane1 = new javax.swing.JSplitPane();
    treePanel = new javax.swing.JPanel();
    jScrollPane1 = new javax.swing.JScrollPane();
    hintsTree = new javax.swing.JTree();
    detailsPanel = new javax.swing.JPanel();
    optionsPanel = new javax.swing.JPanel();
    severityLabel = new javax.swing.JLabel();
    severityCombobox = new javax.swing.JComboBox();
    descriptionPanel = new javax.swing.JPanel();
    jLabel1 = new javax.swing.JLabel();
    jScrollPane2 = new javax.swing.JScrollPane();
    descriptionArea = new javax.swing.JEditorPane();

    setBorder(javax.swing.BorderFactory.createEmptyBorder(8, 8, 8, 8));
    setLayout(new java.awt.GridBagLayout());

    jSplitPane1.setBorder(null);
    jSplitPane1.setDividerLocation(320);
    jSplitPane1.setOpaque(false);

    treePanel.setLayout(new java.awt.BorderLayout());

    jScrollPane1.setViewportView(hintsTree);

    treePanel.add(jScrollPane1, java.awt.BorderLayout.CENTER);

    jSplitPane1.setLeftComponent(treePanel);

    detailsPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 6, 0, 0));
    detailsPanel.setLayout(new java.awt.GridBagLayout());

    severityLabel.setLabelFor(severityCombobox);
    org.openide.awt.Mnemonics.setLocalizedText(severityLabel, org.openide.util.NbBundle.getMessage(AntPanel.class, "LBL_SeverityCombobox")); // NOI18N

    severityCombobox.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        severityComboboxActionPerformed(evt);
      }
    });

    javax.swing.GroupLayout optionsPanelLayout = new javax.swing.GroupLayout(optionsPanel);
    optionsPanel.setLayout(optionsPanelLayout);
    optionsPanelLayout.setHorizontalGroup(
      optionsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(optionsPanelLayout.createSequentialGroup()
        .addComponent(severityLabel)
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addComponent(severityCombobox, javax.swing.GroupLayout.PREFERRED_SIZE, 174, javax.swing.GroupLayout.PREFERRED_SIZE)
        .addContainerGap(137, Short.MAX_VALUE))
    );
    optionsPanelLayout.setVerticalGroup(
      optionsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(optionsPanelLayout.createSequentialGroup()
        .addGroup(optionsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
          .addComponent(severityLabel)
          .addComponent(severityCombobox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
        .addGap(154, 154, 154))
    );

    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 0;
    gridBagConstraints.gridy = 0;
    gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
    gridBagConstraints.weightx = 1.0;
    gridBagConstraints.weighty = 0.7;
    gridBagConstraints.insets = new java.awt.Insets(0, 0, 12, 0);
    detailsPanel.add(optionsPanel, gridBagConstraints);

    descriptionPanel.setLayout(new java.awt.GridBagLayout());

    jLabel1.setLabelFor(jScrollPane2);
    org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(AntPanel.class, "LBL_DescriptionPanel")); // NOI18N
    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 0;
    gridBagConstraints.gridy = 0;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
    descriptionPanel.add(jLabel1, gridBagConstraints);

    jScrollPane2.setViewportView(descriptionArea);

    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 0;
    gridBagConstraints.gridy = 1;
    gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
    gridBagConstraints.gridheight = java.awt.GridBagConstraints.REMAINDER;
    gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
    gridBagConstraints.weightx = 1.0;
    gridBagConstraints.weighty = 1.0;
    gridBagConstraints.insets = new java.awt.Insets(4, 0, 0, 0);
    descriptionPanel.add(jScrollPane2, gridBagConstraints);

    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridx = 0;
    gridBagConstraints.gridy = 1;
    gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
    gridBagConstraints.weightx = 1.0;
    gridBagConstraints.weighty = 0.3;
    detailsPanel.add(descriptionPanel, gridBagConstraints);

    jSplitPane1.setRightComponent(detailsPanel);

    gridBagConstraints = new java.awt.GridBagConstraints();
    gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
    gridBagConstraints.gridheight = java.awt.GridBagConstraints.REMAINDER;
    gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
    gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
    gridBagConstraints.weightx = 1.0;
    gridBagConstraints.weighty = 1.0;
    add(jSplitPane1, gridBagConstraints);
  }// </editor-fold>//GEN-END:initComponents

  private void severityComboboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_severityComboboxActionPerformed
    // TODO add your handling code here:
  }//GEN-LAST:event_severityComboboxActionPerformed

  private final class AntHintComparator implements Comparator<AntHint> {

    @Override
    public int compare(AntHint o1, AntHint o2) {
      return o1.getConfiguration().getId().compareTo(o2.getConfiguration().getId());
    }
  }

  private final class ComboboxRenderer implements ListCellRenderer {

    @Override
    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {

      final JLabel label = new JLabel();

      if (isSelected) {
        label.setBackground(list.getSelectionBackground());
        label.setOpaque(true);
      }
      if (value instanceof Severity) {
        String text = ""; // NOI18N
        switch ((Severity) value) {
          case HINT:
            text = NbBundle.getMessage(AntPanel.class, "TEXT_ShowAsHint"); // NOI18N
            break;
          case WARNING:
            text = NbBundle.getMessage(AntPanel.class, "TEXT_ShowAsWarning"); // NOI18N
            break;
          case ERROR:
            text = NbBundle.getMessage(AntPanel.class, "TEXT_ShowAsError"); // NOI18N
            break;
        }
        label.setText(text);
      }
      return label;
    }
  }

  private final class HintsTreeRenderer implements TreeCellRenderer {

    @Override
    public Component getTreeCellRendererComponent(final JTree tree, final Object value, final boolean selected,
            final boolean expanded, final boolean leaf, final int row, final boolean hasFocus) {
      renderer.setBackground(selected ? dr.getBackgroundSelectionColor() : dr.getBackgroundNonSelectionColor());
      renderer.setForeground(selected ? dr.getTextSelectionColor() : dr.getTextNonSelectionColor());
      renderer.setEnabled(true);

      final Object data = ((DefaultMutableTreeNode) value).getUserObject();
      if (data instanceof AntHint) {
        final AntHint hint = (AntHint) data;
        renderer.setText(hint.getConfiguration().getDisplayName());
        renderer.setSelected(hint.getConfiguration().isEnabled(logic.getCurrentPrefernces(hint)));
      } else if (data instanceof String) {
        renderer.setText((String) data);
        renderer.setSelected(logic.isSelected((DefaultMutableTreeNode) value));
      }
      return renderer;
    }
  }
  // Variables declaration - do not modify//GEN-BEGIN:variables
  private javax.swing.JEditorPane descriptionArea;
  private javax.swing.JPanel descriptionPanel;
  private javax.swing.JPanel detailsPanel;
  private javax.swing.JTree hintsTree;
  private javax.swing.JLabel jLabel1;
  private javax.swing.JScrollPane jScrollPane1;
  private javax.swing.JScrollPane jScrollPane2;
  private javax.swing.JSplitPane jSplitPane1;
  private javax.swing.JPanel optionsPanel;
  private javax.swing.JComboBox severityCombobox;
  private javax.swing.JLabel severityLabel;
  private javax.swing.JPanel treePanel;
  // End of variables declaration//GEN-END:variables
}
